import type { ComponentStyleIdType, OrderType } from '../../types/interfaces'
import type { QuickSearchTag } from '../search/types'
import type {
  INavigationItem,
  INavigatorBarHeightConfig,
  INavigatorBarItemConfig,
  INavigatorBarProps,
  INavigatorBarStatConfig,
  INavigatorHandlers,
} from './types'
import { UIComponent } from '../../lib/abstract'
import { ElementManager } from '../../lib/element_manager'
import { MergeHandler } from '../../lib/merge_handler'
import { ToolKit } from '../../lib/utils/toolkit'
import {
  BodyClasses,
  BUILD_CONFIG,
  ComponentStyleId,
  logger,
  PROJECT_PREFIX,
} from '../../types/constants'
import { SearchBox } from '../search'
import { SortOrder } from '../sort_order'
import { styles } from './styles'
import {
  DEFAULT_NAVIGATOR_HEIGHT,
  DEFAULT_NAVIGATOR_PROPS,
  generateBadgeStyles,
  LIST_ITEM_DATA_ATTRS,
} from './types'

export class NavigatorBar<T extends INavigationItem = INavigationItem> extends UIComponent<INavigatorBarProps<T>> {
  id: string = 'navigator-bar'
  readonly description: string = '导航器'
  readonly styleId: string = ComponentStyleId[this.name]
  private searchBox: SearchBox
  private sortOrder: OrderType
  private sortType: string
  private isDropdownOpen: boolean = false
  private itemsCount: number = 0
  private defaultId: number = 1
  private currentIndex: number = this.defaultId
  private currentItems: T[] = []
  private sortOrderComponent: SortOrder
  private searchQuery: string = ''
  private isQuickTagSearch: boolean = false
  private isFirstUpdateCurrentItem: boolean = true
  private handlers: INavigatorHandlers<T>
  private readonly heightOptions: INavigatorBarHeightConfig
  private highlightClassName: string = `highlighted-animate`
  private resultCount: number = 0
  private resultId: number[] = []
  private resultTextElement: JQuery<HTMLElement> | null = null
  get name(): ComponentStyleIdType {
    return 'NavigatorBar'
  }

  get className(): string {
    return `${PROJECT_PREFIX}-${this.props.id}`
  }

  constructor(props: INavigatorBarProps<T>) {
    super(props)

    // eslint-disable-next-line ts/ban-ts-comment
    // @ts-expect-error
    this.props = MergeHandler.getInstance().deepMerge(DEFAULT_NAVIGATOR_PROPS, props)

    this.handlers = this.props.behavior.handlers
    this.sortOrder = this.props.sortOrder || 'asc'
    this.sortType = this.props.sortType || 'time'
    this.heightOptions = {
      base: this.props.layout.heightOptions?.base || undefined,
      max: this.props.layout.heightOptions?.max || undefined,
      min: this.props.layout.heightOptions?.min,
      dropdownMax: this.props.layout.heightOptions?.dropdownMax,
    }

    // 初始化搜索组件
    this.searchBox = new SearchBox({
      id: this.props.id,
      placeholder: this.props.placeholder,
      showQuickSearch: this.props.features?.showQuickSearch,
      quickSearchTags: this.props.quickSearchTags,
      onSearch: (value, QuickSearch: boolean) => {
        this.searchQuery = value.toLowerCase().trim()
        this.renderList()
        if (QuickSearch) {
          this.isQuickTagSearch = false
        }
      },
      onQuickTagClick: (type) => {
        this.isQuickTagSearch = true
        this.handleQuickSearch(type)
      },
    })

    // 初始化排序组件
    this.sortOrderComponent = new SortOrder({
      order: this.sortOrder,
      type: this.sortType,
      options: this.props.sortOptions || [],
      onChange: (type, order) => {
        this.sortType = type
        this.sortOrder = order
        this.renderList()
      },
    })

    this.registerStyles()
  }

  ELEMENT_SELECTORS = {
    currentItem: { selector: `.current-item`, dynamic: false, unique: false },
  }

  init(): void {
    this.mount()
  }

  destroy(): void {
    this.$element?.off().remove()
    $(document).off('click.navigatorBar')

    super.destroy()
  }

  mount(): void {
    this.$element = this.createNavigatorBar()

    if (!this.$element || !this.$element.length) {
      logger.warn(`[${this.name}] 无法挂载，未找到匹配的选择器: "${this.selector}"`)
      return
    }

    // 1. 先更新 DOM（非常重要！！！）
    this.updateDOM()
    // 2. 获取组件所有元素
    this.getElements()
    // 3. 再绑定事件
    this.bindEvents()
  }

  render(): string {
    return ''
  }

  private createNavigatorBar(): JQuery {
    const positionClass = `position-${this.props.layout.position}`

    return $(`
      <div class="${this.className} ${BodyClasses.IS_EMPTY} ${positionClass}">
        <div class="current-item">
          ${this.renderItemHtml()}
          <div class="navigator-info">
            <span class="current-index">0</span>
            <span class="total-count">/0</span>
          </div>
          <div class="toggle"></div>
        </div>
        <div class="dropdown">
          ${this.props.features?.showSearch ? this.searchBox.render() : ''}
          ${this.props.features?.showSortButton
            ? `
          <div class="sort-box">
              ${this.sortOrderComponent.render()}
          </div>
          `
            : ''}
          <div class="list"></div>
        </div>
      </div>
    `).appendTo(this.props.layout.container || 'body')
  }

  /**
   * 根据配置生成列表项HTML
   */
  renderItemHtml(item?: T): string {
    const renderSection = (section: INavigatorBarItemConfig<T>, data?: T, isRoot = false): string => {
      const containerClass = [section.selector.replace('.', '')]
        .filter(Boolean)
        .join(' ')

      const itemsHtml = section.items.map((itemConfig) => {
        switch (itemConfig.type) {
          case 'text':
          {
            const value = data?.[itemConfig.key!] ?? itemConfig.fallback
            return this.renderTextItem(itemConfig, value)
          }

          case 'link':
          {
            const value = data?.[itemConfig.key!] ?? itemConfig.fallback
            const href = String(data?.[itemConfig.linkSource! as keyof T] || '')
            return this.renderLinkItem(itemConfig, value, href)
          }

          case 'stat':
            return this.renderStatItem(itemConfig, data)

          case 'description':
            return this.renderDescriptionItem(itemConfig, data)

          case 'container':
            return itemConfig.children?.map(child => renderSection(child, data)).join('') || ''

          default:
            return ''
        }
      }).join('')

      // 根容器不需要额外包裹div
      return isRoot ? itemsHtml : `<div class="${containerClass}">${itemsHtml}</div>`
    }

    // 生成头像和内容区域
    const avatarHtml = `<img class="avatar" src="${BUILD_CONFIG.IMAGE.DEFAULT_AVATAR_URL}" alt="">`
    const contentHtml = this.props.display.itemConfig.map(section =>
      renderSection(section, item, false),
    ).join('')

    return item
      ? `<div class="item">${avatarHtml}${contentHtml}</div>`
      : avatarHtml + contentHtml
  }

  private renderTextItem(
    config: { class?: string, format?: (value: any) => string, fallback?: string, syncToTitle?: boolean },
    value: any,
  ): string {
    const content = config.format ? config.format(value) : config.fallback
    return config.class
      ? `<span class="${config.class}" title="${config.syncToTitle ? content : ''}">${content}</span>`
      : content || ''
  }

  private renderLinkItem(
    config: { class?: string, format?: (value: any) => string, fallback?: string },
    value: any,
    href: string,
  ): string {
    const content = config.format ? config.format(value) : config.fallback
    return config.class
      ? `<a class="${config.class}" href="${href}">${content}</a>`
      : content || ''
  }

  private renderStatItem(
    config: { stats?: INavigatorBarStatConfig<T>[] },
    data?: T,
  ): string {
    return config.stats?.map((stat) => {
      const value = data?.[stat.key] ?? stat.defaultValue
      const displayValue = stat.formatter ? stat.formatter(value) : stat.defaultValue

      if (stat.shouldRender && !stat.shouldRender(value)) {
        return ''
      }

      return `
      <span class="stat ${ToolKit.string.camelToKebab(stat.key as string)}">
        <span class="icon">${stat.icon}</span>
        <span class="${stat.contentClass || 'count'}">${displayValue}</span>
      </span>
    `
    }).join('') || ''
  }

  private renderDescriptionItem(
    config: { class?: string, format?: (value: any) => string, fallback?: string, syncToTitle?: boolean },
    data?: T,
  ): string {
    const value = data?.description ?? config.fallback
    const content = config.format ? config.format(value) : config.fallback

    return `
    <div class="${config.class || 'description'}" title="${config.syncToTitle ? content : ''}">
      ${content}
    </div>
  `
  }

  private calculateTagCounts(items: T[], tags: QuickSearchTag[]): QuickSearchTag[] {
    return tags.map((tag) => {
      const count = items.filter(item =>
        tag.matchCondition
          ? tag.matchCondition(item)
          : item.tags?.includes(tag.type),
      ).length
      return { ...tag, count }
    })
  }

  bindEvents(): void {
    // 切换下拉菜单
    this.getElement('currentItem')?.on('click', () => this.toggleDropdown())

    // 点击外部关闭
    $(document).on('click.navigatorBar', (e) => {
      if (!this.$element?.get(0)?.contains(e.target)) {
        this.closeDropdown()
      }
    })

    if (!this.$element)
      return

    // 挂载搜索
    this.searchBox?.mount(this.$element)
    // 挂载排序
    this.sortOrderComponent?.mount(this.$element)

    this.on('navigate', (index) => {
      this.scrollToItem(index)
    })
  }

  private scrollToItem(index: number): void {
    const selector = this.getScrollItemSelector(index)
    const $item = $(selector)
    const itemLength = $item.length

    if (!itemLength)
      return

    if (itemLength > 1)
      logger.warn(`[${this.name}] 索引'${index}'匹配了${itemLength}个元素`, selector)

    // 获取导航菜单高度
    const navMenuHeight = ElementManager.getInstance().getNavbarHeight() as number
    // 获取导航器高度（默认或实际高度）
    const navHeight = this.$element?.outerHeight() || DEFAULT_NAVIGATOR_HEIGHT
    // 计算目标元素位置（相对文档顶部）
    const itemOffset = $item.offset()?.top || 0

    // 添加缓冲空间
    const scrollTo = itemOffset - navMenuHeight - navHeight - 20

    // 平滑滚动
    $('html, body').animate(
      { scrollTop: scrollTo },
      500,
      () => this.highlightItem($item),
    )

    this.highlightItem($item)
  }

  private highlightItem($item: JQuery<HTMLElement>): void {
    // 清除旧动画防止冲突
    $item.off('animationend').removeClass(this.highlightClassName)

    // 滚动监听器
    const scrollHandler = () => {
      const rect = $item[0].getBoundingClientRect()
      const isVisible = rect.top < window.innerHeight && rect.bottom > 0

      // 当元素进入视口时启动动画
      if (isVisible) {
        $item.addClass(this.highlightClassName)
          .on('animationend', () => {
            $item.removeClass(this.highlightClassName)
          })
        $(window).off('scroll', scrollHandler) // 移除监听
      }
    }

    // 绑定滚动事件
    $(window).on('scroll', scrollHandler)

    // 如果已经在视口中立即触发
    scrollHandler()

    // 5秒后自动清理（防内存泄漏）
    setTimeout(() => {
      $(window).off('scroll', scrollHandler)
      $item.removeClass(this.highlightClassName)
    }, 5000)
  }

  handleQuickSearch(type: string): void {
    const label = (this.props.quickSearchTags || []).filter(item => item.type === type)[0]?.label

    if (!type || !label)
      return

    this.searchBox.enableQuickSearch()
    this.searchBox.setValue(label)
  }

  private toggleDropdown(): void {
    this.isDropdownOpen = !this.isDropdownOpen
    this.$element?.toggleClass('expanded', this.isDropdownOpen)
    if (!this.$element?.hasClass('expanded')) {
      this.clearSearch()
    }
  }

  private closeDropdown(): void {
    if ((this.props?.keepDropdownOpen?.() ?? false) === true)
      return

    this.isDropdownOpen = false
    this.$element?.removeClass('expanded')
    this.clearSearch()
  }

  private clearSearch(): void {
    (this.props?.behavior.preventSearchClear?.() ?? true) && this.searchBox.setValue('')
  }

  updateOptions(items: T[]): void {
    this.itemsCount = items.length
    this.currentItems = items

    // 更新 DOM 中的标签显示
    if (this.props.quickSearchTags) {
      this.searchBox.updateQuickSearchTagsCount(this.calculateTagCounts(items, this.props.quickSearchTags))
    }

    let currentIndex = this.currentIndex
    const currentItem = items[currentIndex]

    if (!currentItem) {
      this.setCurrentIndex(this.defaultId)
      currentIndex = this.defaultId
    }

    this.renderList(items)

    this.renderCurrentItem(Math.max(currentIndex, this.defaultId))

    const $container = this.$element?.closest(this.selector)
    const isEmpty = $container?.hasClass(BodyClasses.IS_EMPTY) || true
    if (items.length > 1 && $container && isEmpty) {
      $container.removeClass(BodyClasses.IS_EMPTY)
    }
    else if (items.length === 0 && $container && !isEmpty) {
      $container.addClass(BodyClasses.IS_EMPTY)
    }

    this.updateCounter()
  }

  selectItem(id: number): void {
    this.setCurrentIndex(id)
    this.renderCurrentItem(id)
    this.closeDropdown()
    this.emit('navigate', id)
  }

  private setCurrentIndex(id: number): void {
    this.currentIndex = id
    this.updateCounter()
  }

  private updateCounter(): void {
    this.$element?.find('.current-index').text(this.getListIndexByid(this.currentIndex))
    this.$element?.find('.total-count').text(`/${this.itemsCount}`)
  }

  private getListIndexByid(id: number): string {
    return this.$element?.find(this.getIdSelector(id)).attr(LIST_ITEM_DATA_ATTRS.INDEX) || '1'
  }

  private emit(event: 'navigate', payload: number): void {
    this.$element?.trigger(event, payload)
  }

  on(event: 'navigate', callback: (index: number) => void): this {
    this.$element?.on(event, (_, index) => callback(index))
    return this
  }

  private getIdSelector(id: number) {
    return typeof this.props.currentItemSelectorById === 'function'
      ? this.props.currentItemSelectorById(id)
      : this.props.currentItemSelectorById
  }

  private getIndexSelector(index: number) {
    return typeof this.props.currentItemSelectorByIndex === 'function'
      ? this.props.currentItemSelectorByIndex(index)
      : this.props.currentItemSelectorByIndex
  }

  private getScrollItemSelector(id: number) {
    return typeof this.props.scrollSelector.item === 'function'
      ? this.props.scrollSelector.item(id)
      : this.props.scrollSelector.item
  }

  private getScrollElementSelector() {
    return this.props.scrollSelector.element
  }

  /**
   * 渲染当前项
   * @param id 项索引
   */
  private renderCurrentItem(id: number): void {
    if (!this.$element)
      return

    const $item = this.$element.find(`.current-item`)
    let $currentIndexItem = this.$element.find(this.getIdSelector(id))

    // 首次更新当前项时，若通过id找不到，则尝试使用 index=1 来作为初始值
    if ((!$currentIndexItem || !$currentIndexItem.length || $currentIndexItem.length === 0) && this.isFirstUpdateCurrentItem) {
      $currentIndexItem = this.$element.find(this.getIndexSelector(1))
      this.isFirstUpdateCurrentItem = false
    }

    if (!$currentIndexItem || !$currentIndexItem.length) {
      this.$element.addClass(BodyClasses.IS_EMPTY)
      return
    }

    // 处理基础信息
    this.props.display.baseFields.forEach((field) => {
      const $target = $item.find(field.selector)
      const attrValue = field.attr ? $currentIndexItem.find(field.selector).attr(field.attr) || field.fallback : undefined
      const textValue = field.prop ? $currentIndexItem.find(field.selector).text() || field.fallback : undefined

      if (field.attr && attrValue) {
        $target.attr(field.attr, attrValue)
      }

      if (field.prop && textValue) {
        $target.text(textValue)
      }

      // 同步值到 title 中
      if (field.syncToTitle) {
        $target.attr('title', field.syncToTitle ? textValue || '' : '')
      }
    })

    // 处理徽章状态
    this.props.display.badgeConfig.forEach((badge) => {
      const $badge = $item.find(badge.selector)
      $badge.text(badge.condition ? badge.activeText : badge.inactiveText)
      $badge.toggleClass('active', badge.condition)
    })
  }

  private renderList(items?: T[]): void {
    if (!this.$element)
      return

    const dataSource = items || this.currentItems

    if (!dataSource || dataSource.length === 0) {
      this.$element.find('.list').empty()
      return
    }

    const $list = this.$element.find('.list')
    $list.empty()

    // 应用排序和过滤
    const filteredItems = this.getFilteredItems(items || this.currentItems)
    this.resultCount = filteredItems.length
    this.resultId = filteredItems.map(item => item.id)

    // 渲染结果统计
    this.renderResultCount()

    // 渲染列表项
    filteredItems.forEach((item: T, index: number) => {
      const $item = $(this.renderItemHtml(item))
      this.addRequiredAttributes($item, index + 1, item.id)
      this.addIndex($item, index + 1)
      $list.append($item)
    })

    // 绑定点击事件
    $list.find('.item').on('click', (e) => {
      const index = Number.parseInt($(e.currentTarget).data('id'))
      this.selectItem(index)
    })

    this.afterRenderList(this.resultId)
  }

  afterRenderList(ids: number[]): void {
    this.props?.behavior.afterRenderList?.(ids)
  }

  /**
   * 获取排序和过滤后的列表项
   * @param items 原始列表项
   * @returns 过滤后的列表项
   */
  private getFilteredItems(items: T[]): T[] {
    const sortedItems = this.handlers.sortItems([...items], this.sortType, this.sortOrder)
    return this.searchQuery
      ? sortedItems.filter(item => this.handlers.matchItem(item, this.searchQuery, this.isQuickTagSearch))
      : sortedItems
  }

  private renderResultCount(): void {
    if (!this.resultTextElement) {
      // 首次创建统计元素
      this.resultTextElement = $(`
      <div class="result-count">
        找到 ${this.resultCount} 个结果
      </div>
    `)
      this.$element?.find('.dropdown .search-box').append(this.resultTextElement)
    }
    else {
      // 更新现有元素
      this.resultTextElement.text(`找到 ${this.resultCount} 个结果`)
    }
  }

  /**
   * 确保列表项包含必需的数据属性
   */
  addRequiredAttributes($item: JQuery, index: number, id: string | number): JQuery {
    return $item
      .attr(LIST_ITEM_DATA_ATTRS.INDEX, index.toString())
      .attr(LIST_ITEM_DATA_ATTRS.ID, id.toString())
  }

  /**
   * 添加序号
   */
  addIndex($item: JQuery, index: number): void {
    $item.find('.stats').prepend($(`
      <span class="stat index">
      <span class="content">${index}</span>
    </span>`))
  }

  getStyles(): string {
    return styles({
      className: {
        base: this.className,
        highlight: this.highlightClassName,
      },
      selector: {
        scrollElement: this.getScrollElementSelector(),
      },
      layout: {
        width: this.props.layout.width,
        transform: this.props.layout.transform,
        heightOptions: this.heightOptions,
      },
      quickSearchTags: this.props.quickSearchTags,
      badgeStyles: generateBadgeStyles(this.props.display.badgeStyleConfigs),
      customStyles: this.props.styles,
    })
  }
}
